Repository navigation
sync(upstream): integrate Batch 39 render-pass reads, macOS libkernel, and argument-validation parity - #564
Conversation
(cherry picked from commit 0edfefa)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 22c584a)
(cherry picked from commit 14acf7f)
fcntl threw for F_GETLK with an F_UNLCK request on the hosts that use POSIX locks, as Linux's F_GETLK rejects that type. FreeBSD 11.4's kern_fcntl accepts it and lf_getlock reports the first overlapping lock that lf_blocks lets conflict: another owner's lock, one of the two being a write lock. For an F_UNLCK request that is exactly another owner's write lock, the same set an F_RDLCK request finds, so the host is asked with F_RDLCK and the reply is written back as for the other types. (cherry picked from commit 41ec785)
The fcntl tests only ever held locks in the guest's own process, where FreeBSD's lf_blocks never reports a conflict, so the host lock path that answers F_GETLK was not exercised. A forked process now holds a read lock, a write lock and a write lock to the end of the file through the host. F_GETLK with F_UNLCK reports only the write locks, F_RDLCK the same and F_WRLCK the read lock, each with the owner's pid and SEEK_SET; F_SETLK over the read lock fails with EAGAIN, and nothing is reported once the owner exits. (cherry picked from commit 9ece094)
MeasureStackReserve took the distance from the stack top to a local variable. In an ANYPS5_ENABLE_ASAN build that local lives on AddressSanitizer's fake stack (detect_stack_use_after_return is on by default on Linux), away from the thread's stack, so the reserve could come out negative. scePthreadCreate then asked the guest arena for a stack of nearly 2^64 bytes and threw 'guest address space arena exhausted'. The frame address is on the real stack in every build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit e88b7de)
An ITIMER_REAL or alarm expiry while SIGALRM is blocked is marked on the pending set of sigtimedwait instead of throwing, as kern_psignal leaves a blocked signal pending. sigwait, sigwaitinfo and sigtimedwait take it, repeated expiries merge into one pending signal, and unblocking it runs a guest handler on the unblocking thread or, with the default disposition, exits the process with status 142 as FreeBSD's default action does. The expiry rechecks the mask after marking the signal, so an unblock that races it does not leave SIGALRM pending while unblocked, and TakePending clears the bit with the value it read so one signal is taken once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 1bc029d)
scePthreadCondInit, and pthread_cond_init through it, threw when attr pointed to a null attribute: a zeroed one that was never initialized or one that scePthreadCondattrDestroy has set to null. FreeBSD 11.4 libthr (thr_cond.c, cond_init) takes a null attribute as the default one and creates a CLOCK_REALTIME condition variable. A PS5 (firmware 13.60) takes a zeroed attribute as the default one in scePthreadMutexInit (boykopovar#4158). Take it as the default here too, as scePthreadMutexInit and scePthreadCreate already do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 111d817)
… pthread locks pthread_rwlock_timedrdlock, pthread_rwlock_timedwrlock and pthread_mutex_timedlock threw for a null abstime. FreeBSD's libthr passes a null abstime on to the untimed kernel lock, so these calls then wait like pthread_rwlock_rdlock, pthread_rwlock_wrlock and pthread_mutex_lock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 676ee71)
FreeBSD's getrusage rejects an unknown `who` with EINVAL before it copies anything out and returns EFAULT when the result can't be written. The HLE threw std::invalid_argument for both, which ends the title. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 01c8ceb)
… null buffer read and write already fail with EFAULT for a null buffer and a non-zero length and treat a zero-length null buffer as empty. The sceKernel entry points threw std::invalid_argument for both. They now do the same as read and write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit b437dc5)
… size calls scePthreadAttrGetstack, Getstackaddr, Getguardsize, Setguardsize, Setstack and Setstackaddr threw for a null attribute, a null pointer argument or a stack below the 16 KiB minimum. FreeBSD's libthr returns EINVAL for those, as the pthread_attr_* wrappers here and the other scePthreadAttr calls already do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 19b236a)
…ove SSIZE_MAX FreeBSD's read, write, pread and pwrite reject a byte count above IOSIZE_MAX with EINVAL before they look up the descriptor. write threw std::invalid_argument for a negative count, and the others passed the count on to the host, which reports EFAULT on Linux. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 28acd09)
…tschedpolicy like libthr FreeBSD's libthr rejects a null sched_param and, under SCHED_FIFO or SCHED_RR, a priority outside sched_get_priority_min..max with ENOTSUP, and a policy outside SCHED_FIFO..SCHED_RR with ENOTSUP. The wrappers returned EINVAL for the null parameter and stored any priority or policy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit b693ef2)
…tect for a zero length mprotect already returns 0 for a zero length. The sceKernel entry points passed it to DoMprotect, which throws for an empty range. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit cbf006c)
… a null status (cherry picked from commit a295b77)
…inter clock_gettime threw invalid_argument when the result pointer was null, before looking at the clock id. FreeBSD 11.4's sys_clock_gettime reads the clock through kern_clock_gettime, which fails with EINVAL for an unknown clock, and only then copies the result out, so a null pointer fails with EFAULT. Read the clock into a local value first, then fail with -1 and EFAULT when the pointer is null. A clock that is not supported still throws before the pointer is looked at. Test the null pointer for the supported clocks. Written with AI assistance (Claude). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit c17c6a5)
kevent threw for NOTE_LOWAT. On a socket's EVFILT_READ it now sets the low-water mark as FreeBSD 11's filt_soread does: the event fires once the buffered bytes reach it, or at end of file or on a listening socket, and a later change of the knote replaces it. EVFILT_WRITE takes a mark of 1, where the host's writability stands in for a free byte; other write marks and marks below 1 still throw. A pipe ignores the mark, as filt_piperead does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit e1d4ccb)
(cherry picked from commit 00ca965)
…he PS5 does On a PS5 (firmware 12.70), of the values 0, 1, 2 and -1, pthread_spin_init, pthread_barrierattr_setpshared and pthread_mutexattr_setpshared accept only PTHREAD_PROCESS_PRIVATE (0); 1, 2 and -1 return EINVAL and leave the attribute unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit db504f6)
…D does 66 E8, 66 E9 and 66 0F 80-8F without REX.W take a 16-bit displacement on AMD processors, the console included. Every near relative branch with that prefix truncates its target to 16 bits, so the decoder reports no branch target for it and stubs refuse to move it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016TFMtUC4L4wMS4eeEqaNSR (cherry picked from commit 206aaa5)
…nches A short TLS access followed by a near branch with an operand-size prefix decided the branch was relative from its flow kind and opcode alone, then re-encoded it to the fall-through address or wrote its rel32 over the prefix and opcode. Such a branch now counts as one that cannot move. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016TFMtUC4L4wMS4eeEqaNSR (cherry picked from commit aed1ffb)
…-bit store first A 256-bit store is 4 bytes, so its stub window takes a neighbouring instruction to make room for the jump. The window took the following instruction first even when it was a guest TLS access (an FS or GS prefix). The macOS writer cannot rewrite a TLS access inside a stub and refuses the module with "AMD-only instruction is also a guest TLS access". The window now tries the following and the preceding instruction without a segment prefix first, and falls back to the old order only when neither works, so outputs that can keep the access in the stub do not change. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 513b2e1)
…t store Covers a thread-pointer read (FS and GS form) after the store with a movable instruction before it, and the fallback to the following access when there is no other neighbour. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit cf9e5a2)
…tches (cherry picked from commit 051a610)
DrawPassTotals returns, since the process started, how many recorded draws began a render pass of their own or continued the previous draw's, and how many flushed the queued DCC key stores or label stores before themselves, for address-based draws and for the others. A draw is address-based when its resources hold the allocation registry lease, whether they were built for it or found in the draw cache. The totals are kept with the [draws] line's (APS5_PROFILE_DRAW), so a test or a harness can tell whether an address-based draw kept the open pass without reading the 10-second report. (cherry picked from commit 2d4512e)
…its render pass recordDraw passed every address-based draw to the pass hazard tracker as both an unknown reader and an unknown writer of guest memory. An unknown writer conflicts with any earlier access in the pass, so no address-based draw ever continued the previous draw's pass: each one ended the pass and began a new one, which loads and stores the attachments again. At Resident Evil Village's menu about half of all draws are address-based, and about 590 render passes begin for about 850 draws per frame. A build whose programs only read through the BDA table now reports unknown reads only. The tracker already orders unknown reads behind any recorded range write in the pass and keeps later range writes behind them, so such a draw continues a pass whose earlier draws wrote no guest ranges. Builds with stores through the table, copied writes, or an address lease without a table keep both flags. APS5_NO_ADDRESS_READ_PASSES=1 restores the old behaviour. Render targets reach guest memory only by explicit write-backs, so a read through the table sees the same bytes whether or not the pass breaks. Raghav314's boykopovar#6947 makes the same change inside its GPU descriptor heap, with the read-only fact from boykopovar#6946's recompiler field; this one keeps it in the driver, without a shader cache format change. The idea also comes from fable-void's 5d57298 (wip/new-gpu-path), which orders address-based builds by their known access ranges; this takes the read-only half without his run-time pre-pass bounds. (cherry picked from commit a939efe)
…der pass agc_driver_address_passes draws a triangle twice onto a guest block, with pixel shaders that global_load_dword or global_store_dword through s[2:3], and reads DrawPassTotals for the address-based draws: - --readers: two draws reading through the table share one render pass; - --store-after-read: a draw storing through the table after one reading through it begins its own pass; - --read-sees-store: a draw reading through the table what the previous draw stored sees the store, in a pass of its own. Taken from c44c9a6 (perf/agc-bounded-address-passes) without its --store-beside-reads case, which needs that commit's store bounds. (cherry picked from commit b3e94a5)
…ke the sampled one StorageTextureBudget gave the storage image cache a quarter of the VK_EXT_memory_budget budget of the largest device-local heap (capped at TextureCacheBudget, never below 512 MiB) whatever else the heap held, while SampledTextureBudget takes what that budget leaves after the heap's other use. On a 10 GB card the storage cache kept 2024-2170 MiB into a level load whose depth and render target allocations then failed with VK_ERROR_OUT_OF_DEVICE_MEMORY with the heap at its reported budget where a run logged it. The storage image budget is now what the reported budget leaves after the heap's use other than the cached storage images, the unused part of the sampled textures' 2 GiB floor and an eighth of the budget; the caps at a quarter of the budget and at TextureCacheBudget and the 512 MiB floor stay. The sampled budget still reserves TextureCacheBudget for the storage cache, at least the storage cap, and the storage budget reserves the sampled textures alive, so neither counts bytes the other may take; while the sampled textures alive stay within their floor the sampled budget falls first, then the storage budget, and above it both fall together. The budget line also prints when the storage image budget moves by more than a tenth, with its previous value, and StorageTextureCacheBudget is exported for the execution test. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> (cherry picked from commit 39c2366)
(cherry picked from commit bb39eba)
…a pin wait The Windows release of imports retired during a settle read belongs after the whole overlapsImport block in the not-holding-the-lock branch of WaitForLeasesAndImports, as in the original change, not inside it. It was placed inside while main was merged in. (cherry picked from commit fd301c7)
…ved bits An image_sample_*_a sampler whose dword 3 is built only from values inside the reserved bits 12-29 was refused with "GetSamplerResource dword 3 is not a valid runtime value" when the value was not an OR at the top. The reserved bits of a sample-adjust S# are already dropped term by term from an OR (canonicalizeSampleAdjustDword3); a dword 3 whose possible bits all lie in that mask, such as a shifted field with nothing else, is now dropped whole the same way. PPSA01317 builds such a dword 3 from a wave ballot shifted to bit 12 for image_sample_a in a pixel shader. A plain image_sample with the same S# is still refused. AI-assisted: yes (cherry picked from commit 6e5d708)
select(c, a, select(c, b, d)) is select(c, a, d), and select(c, select(c, a, b), d) is select(c, a, d). The recompiler leaves these behind when a register is written more than once under the same lane mask and when the f32 NaN selection tests the same source twice, and nothing folded them. The constant folder now takes the inner arm the outer condition already decides, then folds a select whose arms became equal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 227d1e4)
On AMD's Windows driver 32.0.21045.5002 (RX 6700 XT), a product of two values the driver knows fit in 16 bits, reduced to 16 bits and stored with nothing else merged in, keeps all 32 bits of the product. v_mul_lo_u16 and v_pk_mul_lo_u16 extracted 16-bit factors, so into a register whose high half was zero they wrote 0xfffe0001 for 0xffff * 0xffff. A factor taken from the low half of its source is now read as the low 24 bits of the register; the low 16 bits of the product depend only on the low 16 bits of the factors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit ffd468e)
macOS does not support getsockopt with SO_ACCEPTCONN; it fails with ENOPROTOOPT. So on macOS the guest's getsockopt of SO_ACCEPTCONN failed, connect did not see that a stream socket was listening (an address of another family failed with EAFNOSUPPORT instead of EOPNOTSUPP), and the read event of a listening socket reported FIONREAD's 0 instead of the waiting connection and did not ignore its low watermark. On macOS the listening state is read from the SO_ACCEPTCONN flag of the socket options that proc_pidfdinfo reports, and the three places that ask go through NativeListening. Other hosts keep getsockopt. (cherry picked from commit 6f2a61a)
getrlimit(RLIMIT_NOFILE) reports the host's limits, and libkernel raised the soft limit to the hard one at load only on Linux. On macOS the soft limit stayed below an unlimited hard limit (1048576 and infinity in a shell here, 256 and infinity by default), so the two differed. XPSemu (PPSA97358) then raises the soft limit to the hard one in QEMU's os_setup_limits, and setrlimit refuses the change. macOS fails with EMFILE at kern.maxfilesperproc (10240 here) whatever the soft limit, even an unlimited one. At load, libkernel now sets both limits to the smaller of the hard limit and kern.maxfilesperproc, so getrlimit reports the descriptor count after which opening fails. The Linux path still raises the soft limit to the hard one. (cherry picked from commit 7391082)
guest_getrlimit failed on macOS because the host's soft and hard RLIMIT_NOFILE differed. On macOS it now also checks that the limit does not exceed kern.maxfilesperproc, and, as on Windows, that guest pipes fail with EMFILE within 64 descriptors of the reported limit. The pipe loop moves into a helper that both use. (cherry picked from commit 82af3e0)
macOS's strtoll and strtoull set errno to EINVAL when the subject has
no digits, as in "", "words" or " +". The guest's integer conversions
pass that to the title, while guest_atoi, guest_wide_conversion and
guest_math expect errno unchanged, as glibc leaves it. So atoi("")
returned 0 with errno 22 on macOS, and all three tests failed there.
A ParseInteger helper in SonyCrt.hpp calls the host's conversion and,
on macOS, restores the caller's errno when nothing was converted and
the base is valid. An overflow still sets ERANGE, and an invalid base
still sets EINVAL. strtol, strtoul, strtoll, strtoull, atoi, atol,
atoll, _Stoull and the wcsto* integer conversions in Strings.cpp use
it. On other hosts the helper calls the conversion unchanged.
(cherry picked from commit 839acfd)
The conversions in RuntimeSupport.cpp call the host's strtoimax,
strtoumax and strtoull directly, so they set EINVAL on macOS for a
subject without digits too. guest_math, once the strtol cases pass,
failed on strtoimax(""). They now go through ParseInteger.
(cherry picked from commit 8469126)
(cherry picked from commit 720ab86)
An earlier sync left an empty-vs-theirs conflict block in the file; keep the storage view members it carried, which later code calls.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Upstream sync batch 39: 41 commits / 32 upstream PRs on top of main (29 from
boykopovar/AnyPS5main since2026-10-17, 3 from open upstream PRs), plus one fork fix for pre-existing conflict markers in
DepthSurface.cpp.the pass counter and its test), fix(agc): let the storage image cache budget follow the heap's use like the sampled one boykopovar/AnyPS5#7071 (storage image cache budget follows the heap's use like the sampled one),
fix(libkernel,agc): unblock RetroArch Windows select and 8_8 UINT boykopovar/AnyPS5#5999 (8_8 UINT color targets decode as R8G8_UINT), perf(agc): settle Windows imports under 1 MiB without waiting boykopovar/AnyPS5#6678 (settle-retired imports released after the overlap block
of a pin wait).
select nested in a select on the same condition), fix(shader): read low-half 16-bit multiply factors as 24-bit fields boykopovar/AnyPS5#7018 (read low-half 16-bit multiply factors as 24-bit fields).
the frame address), fix(libkernel): leave a blocked SIGALRM expiry pending boykopovar/AnyPS5#7289 (leave a blocked SIGALRM expiry pending), fix(libkernel): accept an uninitialized attribute in scePthreadCondInit boykopovar/AnyPS5#7270 (accept an uninitialized attribute in
scePthreadCondInit), fix(libkernel): treat a null deadline as an untimed lock in the timed pthread locks boykopovar/AnyPS5#7035 (a null deadline is an untimed lock in the timed pthread locks), fix(libkernel): return EINVAL and EFAULT from getrusage instead of throwing boykopovar/AnyPS5#7023, fix(libkernel): return EFAULT from sceKernelRead and sceKernelWrite for a null buffer boykopovar/AnyPS5#7025, fix(libkernel): return EINVAL from the scePthreadAttr stack and guard size calls boykopovar/AnyPS5#7026,
fix(libkernel): return EINVAL from read and write for a byte count above SSIZE_MAX boykopovar/AnyPS5#7027, fix(libkernel): return ENOTSUP from pthread_attr_setschedparam and setschedpolicy like libthr boykopovar/AnyPS5#7028, fix(libkernel): read ITIMER_VIRTUAL and ITIMER_PROF as disarmed in getitimer boykopovar/AnyPS5#7032, fix(libkernel): return 0 from sceKernelMprotect and sceKernelMtypeprotect for a zero length boykopovar/AnyPS5#7033, fix(libkernel): return EFAULT from sceKernelWriteThrottlingStatus for a null status boykopovar/AnyPS5#7133, fix(libkernel): return EFAULT from clock_gettime for a null result pointer boykopovar/AnyPS5#7141 (argument validation returns errno instead of throwing), fix(libkernel): take NOTE_LOWAT on socket read and write events boykopovar/AnyPS5#7092
(NOTE_LOWAT on socket events), fix(libkernel): reject port-zero connect on unconnected sockets boykopovar/AnyPS5#6884 (reject connects on listening sockets first), fix(libkernel): reject PTHREAD_PROCESS_SHARED as the PS5 does and add the missing pshared accessors boykopovar/AnyPS5#7295 (reject
PTHREAD_PROCESS_SHARED in the pshared setters as the PS5 does).
(ilogb and ilogbf).
fix(relinker): move a neighbour without an FS or GS prefix with a 256-bit store first boykopovar/AnyPS5#7309 (move a neighbour without an FS or GS prefix with a 256-bit store first).
load on macOS), fix(libc): keep errno when strtol converts nothing on macOS boykopovar/AnyPS5#6853 (keep errno in strtol, strtoimax and _Stoul on macOS).
Reconciliation: the rebase merged the additive test blocks in
libkernel/CMakeLists.txt(guest_pthread_frame_chainfrom main, guest_pthread_attr_stack from boykopovar#7026).
fix(agc): drop committed conflict markers in DepthSurface.cppremoves an empty-vs-theirs conflict block an earlier sync left in the file; it keeps the storage view members the
block carried, which later code calls.
Validation
tools/check_conventions.py --base mainpass, 0 findings;git grep -nE '^(<<<<<<<|>>>>>>>)'empty.agc-mapbit-exact hash51a268f2e73efb9c(stable over the timed repeats);agc-live-map88 px against a main baseline dump (allowed 200, 0 timed repeats over);agc-menu59 px (allowed200, 0 timed repeats over). The unstable-hash warning on the two flickering frames is the known compute-shader race.
Findings